import { Cartesian3 } from '../../Source/Cesium.js';
import { CatmullRomSpline } from '../../Source/Cesium.js';
import { HermiteSpline } from '../../Source/Cesium.js';
import { Math as CesiumMath } from '../../Source/Cesium.js';

describe('Core/CatmullRomSpline', function() {

    var points;
    var times;

    beforeEach(function() {
        points = [
            new Cartesian3(-1.0, -1.0, 0.0),
            new Cartesian3(-0.5, -0.125, 0.0),
            new Cartesian3(0.5, 0.125, 0.0),
            new Cartesian3(1.0, 1.0, 0.0)
        ];
        times = [0.0, 1.0, 2.0, 3.0];
    });

    it('constructor throws without points or times', function() {
        expect(function() {
            return new CatmullRomSpline();
        }).toThrowDeveloperError();
    });

    it('constructor throws when control points length is less than 2', function() {
        expect(function() {
            return new CatmullRomSpline({
                points : [Cartesian3.ZERO]
            });
        }).toThrowDeveloperError();
    });

    it('constructor throws when times.length is not equal to points.length', function() {
        expect(function() {
            return new CatmullRomSpline({
                points : points,
                times : [0.0, 1.0]
            });
        }).toThrowDeveloperError();
    });

    it('sets start and end tangents', function() {
        var start = Cartesian3.subtract(points[1], points[0], new Cartesian3());
        var end = Cartesian3.subtract(points[points.length - 1], points[points.length - 2], new Cartesian3());
        var crs = new CatmullRomSpline({
            points : points,
            times : times,
            firstTangent : start,
            lastTangent : end
        });

        expect(start).toEqual(crs.firstTangent);
        expect(end).toEqual(crs.lastTangent);
    });

    it('computes start and end tangents', function() {
        var controlPoint0 = Cartesian3.clone(points[0]);
        var controlPoint1 = Cartesian3.clone(points[1]);
        var controlPoint2 = Cartesian3.clone(points[2]);

        var start = new Cartesian3();
        start = Cartesian3.multiplyByScalar(Cartesian3.subtract(Cartesian3.subtract(Cartesian3.multiplyByScalar(controlPoint1, 2.0, start), controlPoint2, start), controlPoint0, start), 0.5, start);

        var controlPointn0 = Cartesian3.clone(points[points.length - 1]);
        var controlPointn1 = Cartesian3.clone(points[points.length - 2]);
        var controlPointn2 = Cartesian3.clone(points[points.length - 3]);

        var end = new Cartesian3();
        end = Cartesian3.multiplyByScalar(Cartesian3.add(Cartesian3.subtract(controlPointn0, Cartesian3.multiplyByScalar(controlPointn1, 2.0, end), end), controlPointn2, end), 0.5, end);

        var crs = new CatmullRomSpline({
            points : points,
            times : times
        });

        expect(start).toEqual(crs.firstTangent);
        expect(end).toEqual(crs.lastTangent);
    });

    it('evaluate throws without time', function() {
        var crs = new CatmullRomSpline({
            points : points,
            times : times
        });

        expect(function() {
            crs.evaluate();
        }).toThrowDeveloperError();
    });

    it('evaluate throws when time is out of range', function() {
        var crs = new CatmullRomSpline({
            points : points,
            times : times
        });

        expect(function() {
            crs.evaluate(times[0] - 1.0);
        }).toThrowDeveloperError();
    });

    it('check Catmull-Rom spline against a Hermite spline', function() {
        var crs = new CatmullRomSpline({
            points : points,
            times : times
        });

        var tangents = [crs.firstTangent];
        for ( var i = 1; i < points.length - 1; ++i) {
            tangents.push(Cartesian3.multiplyByScalar(Cartesian3.subtract(points[i + 1], points[i - 1], new Cartesian3()), 0.5, new Cartesian3()));
        }
        tangents.push(crs.lastTangent);

        var hs = HermiteSpline.createC1({
            points : points,
            tangents : tangents,
            times : times
        });

        var granularity = 0.5;
        for ( var j = times[0]; j <= times[points.length - 1]; j = j + granularity) {
            expect(hs.evaluate(j)).toEqualEpsilon(crs.evaluate(j), CesiumMath.EPSILON4);
        }
    });

    it('evaluate with result parameter', function() {
        var crs = new CatmullRomSpline({
            points : points,
            times : times
        });
        var result = new Cartesian3();

        var point = crs.evaluate(times[0], result);
        expect(point).toBe(result);
        expect(result).toEqual(points[0]);
    });

    it('spline with 2 control points defaults to lerp', function() {
        points = points.slice(0, 2);
        times = times.slice(0, 2);

        var crs = new CatmullRomSpline({
            points : points,
            times : times
        });

        var t = (times[0] + times[1]) * 0.5;
        expect(crs.evaluate(t)).toEqual(Cartesian3.lerp(points[0], points[1], t, new Cartesian3()));
    });

    it('spline with 2 control points defaults to lerp and result parameter', function() {
        points = points.slice(0, 2);
        times = times.slice(0, 2);

        var crs = new CatmullRomSpline({
            points : points,
            times : times
        });

        var t = (times[0] + times[1]) * 0.5;
        var result = new Cartesian3();
        var actual = crs.evaluate(t, result);
        expect(actual).toBe(result);
        expect(actual).toEqual(Cartesian3.lerp(points[0], points[1], t, new Cartesian3()));
    });
});
